下面代码输出什么?
1 | @implementation Son : Father |
都输出
Son。self的class和预想的一样,怎么super 的class也是Son?
self 是类的隐藏的参数,指向当前调用方法的类。super 并不是隐藏的参数,它只是一个“编译器指示符”,它和 self 指向的是相同的消息接收者,拿上面的代码为例,不论是用 [self class] 还是 [super class],接收“class”这个消息的接收者都是 当前类 这个对象。不同的是,super 告诉编译器,当调用 class 的方法时,要去调用父类的方法,而不是本类里的。
当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super 时,则从父类的方法列表中开始找。然后调用父类的这个方法
这种机制到底底层是如何实现的?其实当调用类方法的时候,编译器会将方法调用转成一个C函数方法调用,Apple 的 objcRuntimeRef 上说:
1 | Sending Messages |
可以看到会转成调用上面 4 个方法中的一个,由于 _stret 系列的和没有_stret 的那两个类似,先只关注objc_msgSend 和 objc_msgSendSuper 两个方法。当使用 [self class] 调用时,会使用 objc_msgSend 的函数,先看下 objc_msgSend 的函数定义:
1 | id objc_msgSend(id theReceiver, SEL theSelector, ...) |
而当使用 [super class] 调用时,会使用 objc_msgSendSuper 函数,看下 objc_msgSendSuper 的函数定义:
1 | id objc_msgSendSuper(struct objc_super *super, SEL op, ...) |
第一个成员是 receiver, 类似于上面的 objc_msgSend函数第一个参数self
第二个成员是记录当前类的父类是什么,告诉程序从父类中开始找方法,找到方法后,最后内部是使用 objc_msgSend(objc_super->receiver, @selector(class))去调用, 此时已经和[self class]调用相同了,故上述输出结果仍然返回 Son
里面的调用机制大体就是这样了,以上面的分析,回过头来看开始的代码,当输出[self class] 和 [super class] 时,是个怎样的过程。
当使用
[self class]时,这时的self是Son,在使用objc_msgSend时,第一个参数是receiver也就是self。第二个参数,要先找到class这个方法的selector,先从Son这个类开始找,没有,然后到Son的父类Father中去找,也没有,再去Father的父类NSObject去找,一层一层向上找之后,在NSObject的类中发现这个class方法,而NSObject的这个class方法,就是返回receiver的类别,所以这里输出Son。
当使用
[super class]时,这时要转换成objc_msgSendSuper的方法。先构造objc_super的结构体吧,第一个成员变量就是self,第二个成员变量是Father,然后要找class这个selector,先去superClass也就是Father中去找,没有,然后去Father的父类中去找,结果还是在NSObject中找到了。然后内部使用函数objc_msgSend(objc_super->receiver, @selector(class))去调用,此时已经和我们用[self class]调用时相同了,此时的receiver还是Son*son,所以这里返回的也是Son。